from datetime import datetime

from flask import g
from flask_restful import Resource, abort, marshal
from sqlalchemy import desc, and_, or_

from app.api.admin.parsers import get_param_parser
from app.api.wxapp.fields import goods_fields, goods_list_fields, order_fields, order_list_fields, banners_fields, \
    branches_fields, address_fields, goods_info_fields, goods_info_list_fields
from app.api.wxapp.parsers import pagination_parser, sort_parser, filter_parser, add_order_args, branch_filter_parser, \
    update_address_args, add_address_args
from app.exception import APIException, GOODS_NOT_EXISTS, GOODS_NOT_ONLINE, ADDRESS_IS_REQUIRED, \
    DOLL_NOT_ENOUGH, DATABASE_ERROR, TIME_NOT_ENOUGH, BRANCH_NOT_EXISTS, ORDER_NOT_EXISTS, GOODS_NOT_LISTING, \
    ADDRESS_NOT_EXISTS, STOCK_NOT_ENOUGH, BRANCH_NOT_SELECTED
from app.extensions import auth, db
from app.models import Banner, Goods, Order, UserAddress, UserRecord, Branch, GoodsInfo, GoodsRecord, User, GoodsSku, \
    OrderSku, GoodsSkuStock, RemoteArea
from app.utils import generate_order_no


class BannersResource(Resource):
    methods = ['GET', 'OPTIONS']

    # decorators = [auth.login_required]

    @staticmethod
    def branch_exist(branch_id):
        branch = Branch.query.get(branch_id)
        if not branch:
            # abort(404, message='门店(id=%s)不存在' % branch_id, code=404)
            raise APIException(BRANCH_NOT_EXISTS)
        return branch

    def get(self, branch_id):
        args = get_param_parser.parse_args()

        # 未删除 按权重 创建时间排序
        query = Banner.query.filter_by(deleted=0, branch_id=branch_id).order_by(Banner.weights.desc()).order_by(
            Banner.created.asc())

        page_args = pagination_parser.parse_args(req=args)
        page = page_args.get('page')
        per_page = page_args.get('per_page')

        _banners = query.paginate(page, per_page=per_page, error_out=False)
        # _banners = query.all()
        banners = {
            'list': marshal(_banners.items, banners_fields),
            'pageSize': _banners.per_page,
            'pageNo': _banners.page,
            'pages': _banners.pages,
            'total': _banners.total
        }

        return {
                   'code': 0,
                   'message': '',
                   'data': banners
               }, 200


class BranchesResource(Resource):
    methods = ['GET', 'OPTIONS']

    # decorators = [auth.login_required]

    def get(self):
        args = get_param_parser.parse_args()

        query = Branch.query

        _filter = args.get('filter')
        if _filter:
            filter_args = branch_filter_parser.parse_args(req=args)
            keyword = filter_args.pop('keyword')
            if keyword:
                query = query.filter(
                    or_(Branch.name.like("%" + keyword + "%"), Branch.address.like("%" + keyword + "%")))
            else:
                for k, v in filter_args.items():
                    if v is not None:
                        query = query.filter(getattr(Branch, k).like("%" + v + "%"))

        page_args = pagination_parser.parse_args(req=args)
        page = page_args.get('page')
        per_page = page_args.get('per_page')

        _branches = query.paginate(page, per_page=per_page, error_out=False)

        branches = {
            'list': marshal(_branches.items, branches_fields),
            "pageSize": _branches.per_page,
            "pageNo": _branches.page,
            "totalPage": _branches.pages,
            "totalCount": _branches.total
        }

        return {
                   'code': 0,
                   'message': '',
                   'data': branches
               }, 200


class GoodsResource(Resource):
    methods = ['OPTIONS', 'GET']

    # decorators = [auth.login_required]

    @staticmethod
    def goods_exist(goods_id):
        goods = Goods.query.get(goods_id)
        if not goods:
            # abort(404, message='商品(id=%s)不存在' % goods_id, code=404)
            raise APIException(GOODS_NOT_EXISTS)
        return goods

    def get(self, goods_id):
        goods = self.goods_exist(goods_id)
        return {
           'code': 0,
           'message': '',
           'data': marshal(goods, goods_fields)
        }, 200


class GoodsListResource(Resource):
    methods = ['GET', 'OPTIONS']

    # decorators = [auth.login_required]

    @staticmethod
    def branch_exist(branch_id):
        branch = Branch.query.get(branch_id)
        if not branch:
            # abort(404, message='门店(id=%s)不存在' % branch_id, code=404)
            raise APIException(BRANCH_NOT_EXISTS)
        return branch

    def get(self, branch_id):
        args = get_param_parser.parse_args()

        query = GoodsInfo.query.outerjoin(Goods, GoodsInfo.goods_id == Goods.id)\
            .filter(Goods.deleted == 0)\
            .filter(and_(GoodsInfo.branch_id == branch_id, GoodsInfo.is_listing == 1))
        # query = Goods.query

        _filter = args.get('filter')
        if _filter:
            filter_args = filter_parser.parse_args(req=args)
            name = filter_args.pop('name')
            if name:
                query = query.filter(Goods.name.like("%" + name + "%"))
            barcode = filter_args.pop('barcode')
            if barcode:
                query = query.filter(Goods.barcode.like("%" + barcode + "%"))
            recommended = filter_args.pop('recommended')
            if recommended == 1:
                query = query.filter(GoodsInfo.recommended == recommended)
            is_online = filter_args.pop('is_online')  # 0：线下门店 1：线上直邮 2：线上拼单
            if is_online is not None:
                query = query.filter(Goods.is_online == is_online)
            category_id = filter_args.pop('category_id')
            if category_id is not None and category_id > 0:
                query = query.filter(Goods.category_id == category_id)
            secondary_category_id = filter_args.pop('secondary_category_id')
            if secondary_category_id is not None and secondary_category_id > 0:
                query = query.filter(Goods.secondary_category_id == secondary_category_id)

        _sort = args.get('sort')
        if _sort:
            sort_args = sort_parser.parse_args(req=args)
            field = getattr(GoodsInfo, sort_args.get('field'))
            if sort_args.get('field') == 'exchange_num':
                field = getattr(Goods, 'total_num')
            if sort_args.get('reverse'):
                field = desc(field)
            query = query.order_by(field)

        page_args = pagination_parser.parse_args(req=args)
        page = page_args.get('page')
        per_page = page_args.get('per_page')

        _goods = query.paginate(page, per_page=per_page, error_out=False)

        goods = {
            'list': marshal(_goods.items, goods_info_list_fields),
            "pageSize": _goods.per_page,
            "pageNo": _goods.page,
            "pages": _goods.pages,
            "total": _goods.total
        }

        return {
                   'code': 0,
                   'message': '',
                   'data': goods
               }, 200


class GoodsInfoResource(Resource):
    methods = ['OPTIONS', 'GET']

    # decorators = [auth.login_required]

    def get(self, branch_id, goods_id):
        query = GoodsInfo.query.filter_by(branch_id=branch_id).filter_by(goods_id=goods_id)
        goods_info = query.first()
        if goods_info is None or goods_info.goods.deleted:
            raise APIException(GOODS_NOT_EXISTS)

        for item in goods_info.sku_stock:
            if item.stock != 0:
                v_names = []
                for attr in item.sku.attrs:
                    v_names.append(attr.v_name)
                item.sku.suk_name = '-'.join(v_names)
        tree_sql = 'select k_id, k_name from t_goods_sku_attribute where sku_id in (select id from t_goods_sku ' \
                   'WHERE goods_id = %s) group by k_id' % goods_id
        results = db.session.execute(tree_sql).fetchall()

        sku_attrs = [dict(zip(result.keys(), result)) for result in results]

        for value in sku_attrs:
            sql2 = 'select v_id, v_name from t_goods_sku_attribute where sku_id in ' \
                   '(select id from t_goods_sku WHERE goods_id = :goods_id) and k_id = :k_id ' \
                   'group by k_id, v_id  ORDER BY v_id'
            result = db.session.execute(sql2, {'goods_id': goods_id, 'k_id': value['k_id']}).fetchall()
            value['value_list'] = result
        goods_info.sku_attrs = sku_attrs

        return {
           'code': 0,
           'message': '',
           'data': marshal(goods_info, goods_info_fields)
        }, 200


class OrderResource(Resource):
    methods = ['OPTIONS', 'GET']
    decorators = [auth.login_required]

    @staticmethod
    def order_exist(order_id):
        order = Order.query.get(order_id)
        if not order:
            # abort(404, message='订单(id=%s)不存在' % order_id, code=404)
            raise APIException(ORDER_NOT_EXISTS)
        return order

    def get(self, order_id):
        order = self.order_exist(order_id)
        return {
                   'code': 0,
                   'message': '',
                   'data': marshal(order, order_fields)
               }, 200


class OrderListResource(Resource):
    methods = ['GET', 'OPTIONS', 'POST']
    decorators = [auth.login_required]

    @staticmethod
    def branch_exist(branch_id):
        branch = Branch.query.get(branch_id)
        if not branch:
            # abort(404, message='门店(id=%s)不存在' % branch_id, code=404)
            raise APIException(BRANCH_NOT_EXISTS)
        return branch

    def get(self, branch_id):
        args = get_param_parser.parse_args()
        user = g.user
        query = Order.query.filter_by(user_id=user.id).order_by(Order.created.desc())

        page_args = pagination_parser.parse_args(req=args)
        page = page_args.get('page')
        per_page = page_args.get('per_page')

        _orders = query.paginate(page, per_page=per_page, error_out=False)

        for order in _orders.items:
            if order.order_sku.sku:
                order.goods.pic_url = order.order_sku.sku.cover

        orders = {
            'list': marshal(_orders.items, order_list_fields),
            "pageSize": _orders.per_page,
            "pageNo": _orders.page,
            "pages": _orders.pages,
            "total": _orders.total
        }

        return {
           'code': 0,
           'message': '',
           'data': orders
        }, 200

    def post(self, branch_id):
        if not branch_id:
            raise APIException(BRANCH_NOT_SELECTED)

        args = add_order_args.parse_args()
        goods_id = args.get('goods_id')
        goods = Goods.query.get(goods_id)
        info = GoodsInfo.query.filter_by(goods_id=goods_id, branch_id=branch_id).first()

        if not goods or goods.deleted or info is None:
            raise APIException(GOODS_NOT_EXISTS)

        user = g.user

        amount = args.get('amount')
        total_price = goods.exchange_price * amount

        sku_id = args.get('sku_id')
        if sku_id:
            # 如果有sku 计算sku价格
            sku_stock = GoodsSkuStock.query.filter_by(sku_id=sku_id).first()
            if not sku_stock:
                raise APIException(GOODS_NOT_EXISTS)
            total_price = sku_stock.sku.price * amount

        if info.is_listing == 0:
            raise APIException(GOODS_NOT_LISTING)

        if user.doll < total_price:
            raise APIException(DOLL_NOT_ENOUGH)

        last_time = db.session.query(Order.created).order_by(Order.created.desc()).first()[0]
        now_time = datetime.now()
        second = (now_time - last_time).seconds
        if second <= 3:
            raise APIException(TIME_NOT_ENOUGH)

        # 创建订单
        exchange_type = args.get('exchange_type')

        if exchange_type == 0:  # 线下兑换
            if not sku_stock:
                if info.stock < amount:
                    raise APIException(STOCK_NOT_ENOUGH)
            else:
                if sku_stock.stock < amount:
                    raise APIException(STOCK_NOT_ENOUGH)
            info.stock -= amount
            info.update(False, )
            sku_stock.stock -= amount
            sku_stock.update(False, )
            address_id = 0
            status = 0
            if info.stock <= 0:
                info.is_listing = 0
                info.update(False, )
            # 插入商品库存变更记录
            stock_row = {
                'stock_id': info.id,
                'type': 2,
                'increment': -amount
            }
            GoodsRecord.create(False, **stock_row)

        elif exchange_type == 1:  # 线上直邮
            address_id = args.get('address_id')
            if not address_id:
                raise APIException(ADDRESS_IS_REQUIRED)
            # 计算邮费
            fee_template = goods.fee_template
            address = UserAddress.query.get(address_id)
            result = address.address.split(',')
            area = RemoteArea.query.filter(or_(RemoteArea.name.like("%" + result[0] + "%"),
                                               RemoteArea.name.like("%" + address.province + "%"))).first()
            total_price += fee_template.base_fee
            if area:
                total_price += fee_template.add_fee
            status = 1

            # 再次判断用户娃娃数
            if user.doll < total_price:
                raise APIException(DOLL_NOT_ENOUGH)

        elif exchange_type == 2:  # 线上拼兑
            address_id = args.get('address_id')
            if not address_id:
                raise APIException(ADDRESS_IS_REQUIRED)
            status = 6
            if goods.required_number >= amount:
                goods.required_number -= amount
                goods.update(False, )

        order_no = generate_order_no(goods.sku, user.id)
        remark = args.get('remark')
        row = {
            'order_no': order_no,
            'goods_id': goods_id,
            'user_id': user.id,
            'branch_id': branch_id,
            'order_time': datetime.now(),
            'exchange_type': exchange_type,
            'status': status,
            'amount': amount,
            'unit_price': goods.exchange_price,
            'total_price': total_price,
            'address_id': address_id,
            'remark': remark
        }
        order = Order.create(False, **row)

        if sku_stock:
            order_sku_row = {
                'order_id': order.id,
                'sku_id': sku_stock.sku_id,
                'price': sku_stock.sku.price,
                'quantity': amount
            }
            order_sku = OrderSku.create(False, **order_sku_row)
        # 更新用户娃娃
        user.doll = user.doll - total_price
        user.update(False, )
        # 更新已换数量
        info.exchange_num += amount
        info.update(False, )
        goods.total_num += amount
        goods.update(False, )
        # 插入用户娃娃变更记录
        record_row = {
            'user_id': user.id,
            'type': 2 if exchange_type == 1 else 3,
            'doll': -total_price,
            'branch_id':branch_id
        }
        UserRecord.create(False, **record_row)

        try:
            db.session.commit()
        except Exception as e:
            db.session.rollback()
            raise APIException(DATABASE_ERROR)

        return {
           'code': 0,
           'message': '创建成功',
           'data': {}
        }, 201


class UserAddressResource(Resource):
    methods = ['OPTIONS', 'GET', 'PUT', 'DELETE']
    decorators = [auth.login_required]

    @staticmethod
    def address_exist(address_id):
        address = UserAddress.query.get(address_id)
        if not address:
            raise APIException(ADDRESS_NOT_EXISTS)
        return address

    def get(self, address_id):
        address = self.address_exist(address_id)

        return {
           'code': 0,
           'message': '',
           'data': marshal(address, address_fields)
        }, 200

    def put(self, address_id):
        address = self.address_exist(address_id)
        args = update_address_args.parse_args()

        address.update(True, **args)

        return {
           'code': 0,
           'message': '更新成功',
           'data': marshal(address, address_fields)
        }, 200

    def delete(self, address_id):
        address = self.address_exist(address_id)
        address.update(True, deleted=1)
        return {
           'code': 0,
           'message': '删除成功'
        }, 200


class UserAddressesResource(Resource):
    methods = ['GET', 'OPTIONS']
    decorators = [auth.login_required]

    def get(self, user_id):
        args = get_param_parser.parse_args()

        query = UserAddress.query.filter(UserAddress.user_id == user_id, UserAddress.deleted == 0)

        page_args = pagination_parser.parse_args(req=args)
        page = page_args.get('page')
        per_page = page_args.get('per_page')

        _addresses = query.paginate(page, per_page=per_page, error_out=False)

        addresses = {
            'data': marshal(_addresses.items, address_fields),
            "pageSize": _addresses.per_page,
            "pageNo": _addresses.page,
            "totalPage": _addresses.pages,
            "totalCount": _addresses.total
        }

        return {
           'code': 0,
           'message': '',
           'data': addresses
        }, 200


class UserAddressListResource(Resource):
    methods = ['OPTIONS', 'POST']
    decorators = [auth.login_required]

    def post(self):
        args = add_address_args.parse_args()

        last_time = db.session.query(UserAddress.created).order_by(UserAddress.created.desc()).first()[0]
        now_time = datetime.now()
        second = (now_time - last_time).seconds
        if second <= 5:
            raise APIException(TIME_NOT_ENOUGH)

        address = UserAddress.create(True, **args)

        return {
           'code': 0,
           'message': '创建成功',
           'data': marshal(address, address_fields)
        }, 201


class CategoryResource(Resource):
    methods = ['GET', 'OPTIONS']

    # decorators = [auth.login_required]

    @staticmethod
    def branch_exist(branch_id):
        branch = Branch.query.get(branch_id)
        if not branch:
            # abort(404, message='门店(id=%s)不存在' % branch_id, code=404)
            raise APIException(BRANCH_NOT_EXISTS)
        return branch

    def get(self, branch_id):
        args = get_param_parser.parse_args()

        query = Goods.query.outerjoin(GoodsInfo, GoodsInfo.goods_id == Goods.id).filter(
            and_(GoodsInfo.branch_id == branch_id, GoodsInfo.is_listing == 1, GoodsInfo.stock > 0)).order_by(
            GoodsInfo.weights.desc()).order_by(GoodsInfo.created.desc()).order_by(Goods.created.desc())

        _filter = args.get('filter')
        if _filter:
            filter_args = filter_parser.parse_args(req=args)
            category_id = filter_args.pop('category_id')
            if category_id:
                query = query.filter(Goods.category_id == category_id)

        _sort = args.get('sort')
        if _sort:
            sort_args = sort_parser.parse_args(req=args)
            field = getattr(Goods, sort_args.get('field'))
            if sort_args.get('reverse'):
                field = desc(field)
            query = query.order_by(field)

        page_args = pagination_parser.parse_args(req=args)
        page = page_args.get('page')
        per_page = page_args.get('per_page')

        _goods = query.paginate(page, per_page=per_page, error_out=False)

        goods = {
            'list': marshal(_goods.items, goods_list_fields),
            "pageSize": _goods.per_page,
            "pageNo": _goods.page,
            "pages": _goods.pages,
            "total": _goods.total
        }

        return {
           'code': 0,
           'message': '',
           'data': goods
        }, 200
